package com.starry.module.system.core.tenant.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.starry.core.common.constants.CommonConstant;
import com.starry.core.common.constants.CommonError;
import com.starry.core.common.domain.vo.PageVo;
import com.starry.core.common.execption.ServiceException;
import com.starry.core.common.tenant.context.TenantContextHolder;
import com.starry.core.redis.service.RedisService;
import com.starry.core.tenant.config.TenantProperties;
import com.starry.db.core.enums.DBEnableDisableStatusEnum;
import com.starry.db.core.query.LambdaQueryWrapperX;
import com.starry.module.system.api.tenant.domain.qo.SysTenantPageQo;
import com.starry.module.system.api.tenant.domain.qo.SysTenantQo;
import com.starry.module.system.api.tenant.domain.vo.SysTenantDtlVo;
import com.starry.module.system.api.tenant.domain.vo.SysTenantVo;
import com.starry.module.system.core.role.service.impl.SysRoleServiceImpl;
import com.starry.module.system.core.tenant.entity.SysTenant;
import com.starry.module.system.core.tenant.mapper.SysTenantMapper;
import com.starry.module.system.core.tenant.service.SysTenantPackageService;
import com.starry.module.system.core.tenant.service.SysTenantService;
import com.starry.module.system.core.user.service.SysUserService;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import static com.starry.core.tenant.core.redis.RedisKeyConstants.SYS_TENANT_DETAILS;
import static com.starry.module.system.core.enums.ErrorCodeConstants.*;

/**
 * 系统租户-服务实现
 *
 * @author xiaoke
 */
@Service("sysTenantService")
public class SysTenantServiceImpl implements SysTenantService {

    private final SysTenantMapper sysTenantMapper;
    private final SysUserService sysUserService;
    private final SysTenantPackageService sysTenantPackageService;
    private final RedisService redisService;
    private final TenantProperties tenantProperties;
    private final SysRoleServiceImpl sysRoleService;

    @Lazy
    public SysTenantServiceImpl(SysTenantMapper sysTenantMapper, SysUserService sysUserService, SysTenantPackageService sysTenantPackageService, RedisService redisService, TenantProperties tenantProperties, SysRoleServiceImpl sysRoleService) {
        this.sysTenantMapper = sysTenantMapper;
        this.sysUserService = sysUserService;
        this.sysTenantPackageService = sysTenantPackageService;
        this.redisService = redisService;
        this.tenantProperties = tenantProperties;
        this.sysRoleService = sysRoleService;
    }

    @Override
    public PageVo<SysTenantVo> page(SysTenantPageQo pageQo) {
        PageVo<SysTenant> page = sysTenantMapper.selectPage(pageQo);
        List<SysTenantVo> vos = BeanUtil.copyToList(page.getRows(), SysTenantVo.class);
        return new PageVo<>(vos, page.getTotal());
    }

    @Override
    public SysTenantDtlVo getById(Long id) {
        SysTenant sysTenant = sysTenantMapper.selectById(id);
        if (sysTenant == null) {
            throw new ServiceException(CommonError.DATA_NOT_EXISTS.getMsg());
        }
        return BeanUtil.toBean(sysTenant, SysTenantDtlVo.class);
    }

    @Transactional
    @Override
    public Long save(SysTenantQo sysTenantQo) {
        LambdaQueryWrapperX<SysTenant> wrapper = new LambdaQueryWrapperX<>();
        wrapper
                .eq(SysTenant::getName, sysTenantQo.getName());
        if (sysTenantMapper.selectCount(wrapper) > 0) {
            throw new ServiceException(CommonError.DATA_EXISTS.getMsg());
        }
        // 校验
        validate(sysTenantQo);

        SysTenant sysTenant = BeanUtil.toBean(sysTenantQo, SysTenant.class);
        sysTenant.setId(null);
        sysTenantMapper.insert(sysTenant);

        // 插入租户基础信息
        insertTenantBaseInfo(sysTenant.getId());

        return sysTenant.getId();
    }

    @Override
    public Long getByWebSiteTenantId(String webSite) {
        String url = webSite.replaceFirst("^https?://", "");
        SysTenant sysTenants = sysTenantMapper.selectByWebsite(url);
        if (sysTenants == null) {
            return null;
        }
        return sysTenants.getId();
    }

    @Override
    public Set<Long> getTenantPackageMenuIds(Long tenantId) {
        SysTenantDtlVo tenant = getById(tenantId);
        return sysTenantPackageService.getTenantPackageMenuIds(tenant.getPackageId());
    }

    @Override
    public boolean validateAccountCount(Long userCount, Long tenantId) {
        // 校验是否为系统租户
        if (CommonConstant.SYSTEM_TENANT_ID.equals(tenantId)) return true;

        SysTenant sysTenant = sysTenantMapper.selectById(tenantId);
        if (Objects.isNull(sysTenant)) return false;
        return userCount < sysTenant.getAccountCount();
    }

    @Transactional
    @Override
    public void updateById(SysTenantQo sysTenantQo) {
        if (sysTenantQo.getId() == null) {
            throw new ServiceException(CommonError.DATA_NOT_EXISTS.getMsg());
        }

        // 校验是否为系统租户
        validateWhetherSystemTenant(sysTenantQo.getId());
        // 校验
        validate(sysTenantQo);

        LambdaQueryWrapperX<SysTenant> wrapper = new LambdaQueryWrapperX<>();
        wrapper
                .ne(SysTenant::getId, sysTenantQo.getId())
                .and(w ->
                        w.eq(SysTenant::getName, sysTenantQo.getName())
                );
        if (sysTenantMapper.selectCount(wrapper) > 0) {
            throw new ServiceException(CommonError.DATA_EXISTS.getMsg());
        }
        SysTenant sysTenant = sysTenantMapper.selectById(sysTenantQo.getId());
        if (sysTenant == null) {
            throw new ServiceException(CommonError.DATA_NOT_EXISTS.getMsg());
        }
        BeanUtil.copyProperties(sysTenantQo, sysTenant);
        sysTenantMapper.updateById(sysTenant);

        // 清除缓存
        clearCache(sysTenantQo.getId());
    }

    @Transactional
    @Override
    public void removeByIds(Set<Long> idList) {
        if (idList.isEmpty()) return;

        // 校验是否为系统租户
        validateWhetherSystemTenant(idList);
        sysTenantMapper.deleteByIds(idList);

        // 清除缓存
        idList.forEach(this::clearCache);
    }

    @Transactional
    @Override
    public void enable(Set<Long> idList) {
        if (idList.isEmpty()) return;

        // 校验是否为系统租户
        validateWhetherSystemTenant(idList);

        sysTenantMapper.updateBatch(setTenantStatus(idList, DBEnableDisableStatusEnum.ENABLE));
    }

    @Transactional
    @Override
    public void disable(Set<Long> idList) {
        if (idList.isEmpty()) return;

        // 校验是否为系统租户
        validateWhetherSystemTenant(idList);

        sysTenantMapper.updateBatch(setTenantStatus(idList, DBEnableDisableStatusEnum.DISABLE));
    }

    @Override
    public boolean validateTenant() {
        if (!tenantProperties.getEnable()) return true;
        Long tenantId;
        try {
            tenantId = TenantContextHolder.getRequiredTenantId();
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
        if (TenantContextHolder.validateIsSystemTenant()) {
            return true;
        }
        SysTenant tenant = sysTenantMapper.selectById(tenantId);
        if (tenant == null) {
            throw new ServiceException(TENANT_NOT_EXISTS);
        }
        if (DBEnableDisableStatusEnum.isDisable(tenant.getStatus())) {
            throw new ServiceException(TENANT_NOT_ENABLE);
        }
        if (tenant.getExpireTime() != null && tenant.getExpireTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(TENANT_EXPIRE);
        }
        return true;
    }

    private Set<SysTenant> setTenantStatus(Set<Long> idList, DBEnableDisableStatusEnum status) {
        Set<SysTenant> sysTenants = new HashSet<>();
        idList.forEach(item -> {
            SysTenant sysTenant = new SysTenant();
            sysTenant.setId(item);
            sysTenant.setStatus(status.getCode());
            sysTenants.add(sysTenant);

            // 清除缓存
            clearCache(item);
        });
        return sysTenants;
    }

    /**
     * 插入租户基础信息
     *
     * @param id 租户id
     */
    private void insertTenantBaseInfo(Long id) {
        Long oldTenantId = TenantContextHolder.getRequiredTenantId();

        TenantContextHolder.setTenantId(id);

        // 创建租户角色
        Long roleId = sysRoleService.saveTenantRoleAdmin(getTenantPackageMenuIds(id));
        // 创建租户管理员
        sysUserService.saveTenantUserAdmin(Set.of(roleId));

        TenantContextHolder.setTenantId(oldTenantId);
    }

    /**
     * 校验
     *
     * @param sysTenantQo 租户信息
     */
    private void validate(SysTenantQo sysTenantQo) {
        if (!sysTenantPackageService.existTenantPackage(sysTenantQo.getPackageId())) {
            throw new ServiceException("租户套餐不存在");
        }
    }

    /**
     * 校验是否为系统租户
     *
     * @param idList 租户id集合
     */
    private void validateWhetherSystemTenant(Set<Long> idList) {
        idList.forEach(this::validateWhetherSystemTenant);
    }

    /**
     * 校验是否为系统租户
     *
     * @param id 租户id
     */
    private void validateWhetherSystemTenant(Long id) {
        if (Objects.equals(id, CommonConstant.SYSTEM_TENANT_ID)) {
            throw new ServiceException(CommonError.SYSTEM_TENANT_OPERATE_ERROR.getMsg());
        }
    }

    /**
     * 清理缓存
     *
     * @param tenantId 租户id
     */
    private void clearCache(Long tenantId) {
        redisService.deleteObject(SYS_TENANT_DETAILS + tenantId);
    }
}

